home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 24 / CU Amiga Magazine's Super CD-ROM 24 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-07].iso / CUCD / Programming / SWI / source / src / pl-fmt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-07  |  10.3 KB  |  475 lines

  1. /*  $Id: pl-fmt.c,v 1.25 1997/08/07 07:57:54 jan Exp $
  2.  
  3.     Copyright (c) 1990 Jan Wielemaker. All rights reserved.
  4.     See ../LICENCE to find out about your rights.
  5.     jan@swi.psy.uva.nl
  6.  
  7.     Purpose: Formated write
  8. */
  9.  
  10. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  11. Formatted output (Prolog predicates format/[1,2,3]).   One  day,  the  C
  12. source should also use format() to produce error messages, etc.
  13. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14.  
  15. #include "pl-incl.h"
  16. #include "pl-ctype.h"
  17. extern int Output;
  18.  
  19. #define BUFSIZE     10240
  20. #define DEFAULT     (-1)
  21. #define SHIFT       { argc--; argv++; }
  22. #define NEED_ARG    { if ( argc <= 0 ) \
  23.               { ERROR("not enough arguments"); \
  24.               } \
  25.             }
  26. #define ERROR(fmt)    return warning("format/2: %s", fmt)
  27. #define ERROR1(fmt, a)    { char tp[50]; \
  28.               strcpy(tp, "format/2: "); \
  29.               strcat(tp, fmt); \
  30.               return warning(tp, a); \
  31.             }
  32. #define OUTSTRING(s)    { char *q = s; \
  33.               for(; *q; q++) OUTCHR(*q); \
  34.             }
  35. #define OUTCHR(c)    { if ( pending_rubber ) \
  36.                 buffer[index++] = (c); \
  37.               else \
  38.                 Put((Char)(c)); \
  39.               column = update_column(column, c); \
  40.             }
  41.  
  42. #define MAXRUBBER 100
  43.  
  44. struct rubber
  45. { int where;                /* where is rubber in output */
  46.   int size;                /* how big should it be */
  47.   Char pad;                /* padding character */
  48. };
  49.  
  50. #define format_predicates (GD->format.predicates)
  51.  
  52. forwards int    update_column(int, Char);
  53. forwards bool    do_format(const char *fmt, int argc, term_t argv);
  54. forwards void    distribute_rubber(struct rubber *, int, int);
  55. forwards void    emit_rubber(char *buf, int, struct rubber *, int);
  56.  
  57.         /********************************
  58.         *       PROLOG CONNECTION    *
  59.         ********************************/
  60.  
  61. word
  62. pl_format_predicate(term_t chr, term_t descr)
  63. { long c;
  64.   Procedure proc;
  65.   Symbol s;
  66.  
  67.   if ( !PL_get_long(chr, &c) || c < 0 || c > 255 )
  68.   { char *s;
  69.     
  70.     if ( PL_get_atom_chars(chr, &s) && s[0] && !s[1] )
  71.       c = s[0] & 0xff;
  72.     else
  73.       return warning("format_predicate/2: illegal character");
  74.   }
  75.  
  76.   if ( !get_procedure(descr, &proc, 0, GP_CREATE) )
  77.     fail;
  78.   if ( proc->definition->functor->arity == 0 )
  79.     return warning("format_predicate/2: predicate must have at least 1 argument");
  80.  
  81.   if ( !format_predicates )
  82.     format_predicates = newHTable(8);
  83.   
  84.   if ( (s = lookupHTable(format_predicates, (void *)c)) )
  85.     s->value = (word) proc;
  86.   else
  87.     addHTable(format_predicates, (void *)c, proc);
  88.  
  89.   succeed;
  90. }
  91.  
  92.  
  93. word
  94. pl_format(term_t fmt, term_t Args)
  95. { term_t argv;
  96.   int argc = 0;
  97.   char *f;
  98.   int rval;
  99.   term_t args = PL_copy_term_ref(Args);
  100.   
  101.   if ( !PL_get_chars(fmt, &f, CVT_ALL|BUF_RING) )
  102.     return warning("format/2: format is not an atom or string");
  103.  
  104.   if ( (argc = lengthList(args)) >= 0 )
  105.   { term_t head = PL_new_term_ref();
  106.     int n = 0;
  107.  
  108.     argv = PL_new_term_refs(argc);
  109.     while( PL_get_list(args, head, args) )
  110.       PL_put_term(argv+n++, head);
  111.   } else
  112.   { argc = 1;
  113.     argv = PL_new_term_refs(argc);
  114.  
  115.     PL_put_term(argv, args);
  116.   }
  117.   
  118.   rval = do_format(f, argc, argv);
  119.  
  120.   return rval;
  121. }
  122.  
  123.  
  124. word
  125. pl_format3(term_t stream, term_t fmt, term_t args)
  126. { streamOutput(stream, pl_format(fmt, args));
  127. }
  128.  
  129. #if O_C_FORMAT
  130.  
  131.         /********************************
  132.         *          C-CONNECTION        *
  133.         ********************************/
  134.  
  135. static bool
  136. vformat(fm, args)
  137. char *fm;
  138. va_list args;
  139. }
  140.  
  141.  
  142. bool
  143. format(char *fm, ...)
  144. { va_list args;
  145.   bool rval;
  146.  
  147.   va_start(args, fm);
  148.   rval = vformat(fm, args);
  149.   va_end(args);
  150.  
  151.   return rval;
  152. }
  153.  
  154. #endif /* O_C_FORMAT */
  155.  
  156.         /********************************
  157.         *       ACTUAL FORMATTING    *
  158.         ********************************/
  159.  
  160. static int
  161. update_column(int col, int c)
  162. { switch(c)
  163.   { case '\n':    return 0;
  164.     case '\t':    return (col + 1) | 0x7;
  165.     case '\b':    return (col <= 0 ? 0 : col - 1);
  166.     default:    return col + 1;
  167.   }
  168. }   
  169.  
  170.  
  171. static bool
  172. do_format(const char *fmt, int argc, term_t argv)
  173. { char buffer[BUFSIZE];            /* to store chars with tabs */
  174.   int index = 0;            /* index in buffer */
  175.   int column = currentLinePosition();    /* current output column */
  176.   int tab_stop = 0;            /* padded tab stop */
  177.   int pending_rubber = 0;        /* number of not-filled ~t's */
  178.   struct rubber rub[MAXRUBBER];
  179.   Symbol s;
  180.  
  181.   LockStream();
  182.  
  183.   while(*fmt)
  184.   { switch(*fmt)
  185.     { case '~':
  186.     { int arg = DEFAULT;        /* Numeric argument */
  187.                     /* Get the numeric argument */
  188.       if ( isDigit(*++fmt) )
  189.       { for( ; isDigit(*fmt); fmt++ )
  190.           arg = (arg == DEFAULT ? arg = *fmt - '0' : arg*10 + *fmt - '0');
  191.       } else if ( *fmt == '*' )
  192.       { NEED_ARG;
  193.         if ( PL_get_integer(argv, &arg) )
  194.         { SHIFT;
  195.         } else
  196.           ERROR("no or negative integer for `*' argument");
  197.         fmt++;
  198.       } else if ( *fmt == '`' )
  199.       { arg = *++fmt;
  200.         fmt++;
  201.       }
  202.         
  203.                     /* Check for user defined format */
  204.       if ( format_predicates &&
  205.            (s = lookupHTable(format_predicates, (Void)((long)*fmt))) )
  206.       { Procedure proc = (Procedure) s->value;
  207.         FunctorDef fdef = proc->definition->functor;
  208.         term_t av = PL_new_term_refs(fdef->arity);
  209.         char buf[BUFSIZE];
  210.         char *str = buf;
  211.         int i;
  212.         qid_t qid;
  213.  
  214.         if ( arg == DEFAULT )
  215.           PL_put_atom(av+0, ATOM_default);
  216.         else
  217.           PL_put_integer(av+0, arg);
  218.  
  219.         for(i=1; i<fdef->arity; i++)
  220.         { NEED_ARG;
  221.           PL_put_term(av+i, argv);
  222.           SHIFT;
  223.         }
  224.  
  225.         tellString(&str, BUFSIZE);
  226.         qid = PL_open_query(proc->definition->module, PL_Q_NODEBUG,
  227.                 proc, av);
  228.         PL_next_solution(qid);
  229.         PL_close_query(qid);
  230.         toldString();
  231.         OUTSTRING(str);
  232.         if ( str != buf )
  233.           free(str);
  234.  
  235.         fmt++;
  236.       } else
  237.       { switch(*fmt)        /* Build in formatting */
  238.         { case 'a':            /* atomic */
  239.         { char *s;
  240.  
  241.           NEED_ARG;
  242.           if ( !PL_get_chars(argv, &s, CVT_ATOMIC) )
  243.             ERROR("illegal argument to ~a");
  244.           SHIFT;
  245.           OUTSTRING(s);
  246.           fmt++;
  247.           break;
  248.         }
  249.           case 'c':            /* ascii */
  250.         { int c;
  251.  
  252.           NEED_ARG;
  253.           if ( PL_get_integer(argv, &c) && c>=0 && c<=255 )
  254.           { int times = (arg == DEFAULT ? 1 : arg);
  255.  
  256.             SHIFT;
  257.             while(times-- > 0)
  258.             { OUTCHR(c);
  259.             }
  260.           } else
  261.             ERROR("illegal argument to ~c");
  262.           fmt++;
  263.           break;
  264.         }
  265.           case 'e':            /* exponential float */
  266.           case 'E':            /* Exponential float */
  267.           case 'f':            /* float */
  268.           case 'g':            /* shortest of 'f' and 'e' */
  269.           case 'G':            /* shortest of 'f' and 'E' */
  270.         { double f;
  271.           char tmp[12];
  272.           char buf[256];
  273.  
  274.           NEED_ARG;
  275.           if ( !PL_get_float(argv, &f) )
  276.             ERROR1("illegal argument to ~%c", *fmt);
  277.           SHIFT;
  278.           Ssprintf(tmp, "%%.%d%c", arg == DEFAULT ? 6 : arg, *fmt);
  279.           Ssprintf(buf, tmp, f);
  280.           OUTSTRING(buf);
  281.           fmt++;
  282.           break;
  283.         }
  284.           case 'd':            /* integer */
  285.           case 'D':            /* grouped integer */
  286.           case 'r':            /* radix number */
  287.           case 'R':            /* Radix number */
  288.         { int i;
  289.           char tmp[50];
  290.  
  291.           NEED_ARG;
  292.           if ( !PL_get_integer(argv, &i) )
  293.             ERROR1("illegal argument to ~%c", *fmt);
  294.           SHIFT;
  295.           if ( arg == DEFAULT )
  296.             arg = 0;
  297.           if ( *fmt == 'd' || *fmt == 'D' )
  298.             formatInteger(*fmt == 'D', arg, 10, TRUE, i, tmp);
  299.           else
  300.             formatInteger(FALSE, 0, arg, *fmt == 'r', i, tmp);
  301.           OUTSTRING(tmp);            
  302.           fmt++;
  303.           break;
  304.         }
  305.           case 's':            /* string */
  306.         { char *s;
  307.  
  308.           NEED_ARG;
  309.           if ( !PL_get_chars(argv, &s, CVT_LIST|CVT_STRING) )
  310.             ERROR("illegal argument to ~s");
  311.           OUTSTRING(s);
  312.           SHIFT;
  313.           fmt++;
  314.           break;
  315.         }
  316.           case 'i':            /* ignore */
  317.         { NEED_ARG;
  318.           SHIFT;
  319.           fmt++;
  320.           break;
  321.         }
  322.         { Func f;
  323.           char buf[BUFSIZE];
  324.           char *str;
  325.  
  326.           case 'k':            /* write_canonical */
  327.           f = pl_write_canonical; 
  328.               goto pl_common;
  329.           case 'p':            /* print */
  330.           f = pl_print;
  331.               goto pl_common;
  332.           case 'q':            /* writeq */
  333.           f = pl_writeq;
  334.               goto pl_common;
  335.           case 'w':            /* write */
  336.           f = pl_write;
  337.           pl_common:
  338.  
  339.           NEED_ARG;
  340.           if ( pending_rubber )
  341.           { str = buf;
  342.             tellString(&str, BUFSIZE);
  343.             (*f)(argv);
  344.             toldString();
  345.             OUTSTRING(str);
  346.             if ( str != buf )
  347.               free(str);
  348.           } else
  349.           { IOSTREAM *s = PL_current_output();
  350.             if ( s->position && s->position->linepos == column )
  351.             { (*f)(argv);
  352.               column = s->position->linepos;
  353.             } else
  354.             { str = buf;
  355.               tellString(&str, BUFSIZE);
  356.               (*f)(argv);
  357.               toldString();
  358.               OUTSTRING(str);
  359.               if ( str != buf )
  360.             free(str);
  361.             }
  362.           }
  363.           SHIFT;
  364.           fmt++;
  365.           break;
  366.         }
  367.           case '~':            /* ~ */
  368.         { OUTCHR('~');
  369.           fmt++;
  370.           break;
  371.         }
  372.           case 'n':            /* \n */
  373.           case 'N':            /* \n if not on newline */
  374.         { if ( arg == DEFAULT )
  375.             arg = 1;
  376.           if ( *fmt == 'N' && column == 0 )
  377.             arg--;
  378.           while( arg-- > 0 )
  379.             OUTCHR('\n');
  380.           fmt++;
  381.           break;
  382.         }
  383.           case 't':            /* insert tab */
  384.         { rub[pending_rubber].where = index;
  385.           rub[pending_rubber].pad   = (arg == DEFAULT ? (Char) ' '
  386.                                   : (Char) arg);
  387.           pending_rubber++;
  388.           fmt++;
  389.           break;
  390.         }
  391.           case '|':            /* set tab */
  392.         { int stop;
  393.  
  394.           if ( arg == DEFAULT )
  395.             arg = column;
  396.           case '+':            /* tab relative */
  397.           if ( arg == DEFAULT )
  398.             arg = 8;
  399.           stop = (*fmt == '+' ? tab_stop + arg : arg);
  400.  
  401.           if ( pending_rubber == 0 ) /* nothing to distribute */
  402.           { rub[0].where = index;
  403.             rub[0].pad = ' ';
  404.             pending_rubber++;
  405.           }
  406.           distribute_rubber(rub, pending_rubber, stop - column);
  407.           emit_rubber(buffer, index, rub, pending_rubber);
  408.           index = 0;
  409.           pending_rubber = 0;
  410.  
  411.           column = tab_stop = stop;
  412.           fmt++;
  413.           break;
  414.         }
  415.           default:
  416.         ERROR1("unknown format: %c", *fmt);
  417.         }
  418.       }
  419.       break;            /* the '~' switch */
  420.     }
  421.       default:
  422.     { OUTCHR(*fmt);
  423.       fmt++;
  424.       break;
  425.     }
  426.     }
  427.   }
  428.  
  429.   if ( pending_rubber )            /* not closed ~t: flush out */
  430.     emit_rubber(buffer, index, rub, 0);
  431.  
  432.   UnlockStream();
  433.  
  434.   succeed;
  435. }
  436.  
  437. static void
  438. distribute_rubber(struct rubber *r, int rn, int space)
  439. { if ( space > 0 )
  440.   { int s = space / rn;
  441.     int n, m;
  442.  
  443.     for(n=0; n < rn; n++)        /* give them equal size */
  444.       r[n].size = s;
  445.                     /* distribute from the center */
  446.     space -= s*rn;
  447.     for(m = rn / 2, n = 0; space; n++, space--)
  448.     { r[m + (n % 2 ? n : -n)].size++;
  449.     }
  450.   } else
  451.   { int n;
  452.  
  453.     for(n=0; n < rn; n++)        /* set all rubber to 0 */
  454.       r[n].size = 0;
  455.   }
  456. }
  457.  
  458. static void
  459. emit_rubber(char *buf, int i, struct rubber *r, int rn)
  460. { int j;
  461.  
  462.   for(j = 0; j <= i; j++)
  463.   { if ( r->where == j && rn )
  464.     { int n;
  465.       for(n=0; n<r->size; n++)
  466.         Put(r->pad);
  467.       r++;
  468.       rn--;
  469.     }
  470.     if ( j < i )
  471.       Put(buf[j]);
  472.   }
  473. }
  474.